《windows PE》延迟加载导入表

预备知识

1.延迟加载导入的概念及作用

         延迟加载导入表和导入表示相互分离的,延迟加载导入表是特殊的导入表,和导入表不同的是,延迟加载导入表所记录的dll不会被操作系统加载,只有在函数被应用程序调用的时候,PE中注册的延迟加载函数才会根据延迟加载导入表的记录,动态加载dll,以及修正导入函数的VA。

2.延迟加载的优势

         延迟加载由于没有在程序初始化的时候初始化dll,只是会在应用程序调用某个模块的时候加载该模块,所以使用延迟加载技术的程序拥有更高的初始化速度。
         通过延迟加载的方法,提高了程序的兼容性,原因在于基于延迟加载,所用到的模块不需要在初始化前加载,保证了程序能够运行成功,如果缺少模块或者缺少模
块里面的函数造成的异常,可以使得编译器单独处理该某个dll或者某个函数的调用。

PE中的延迟加载导入表

1.延迟加载导入表的定位

         延迟加载导入表的描述信息在数目目录的第14个目录项中,定位方法和前面的导入导出表一致。

2.延迟加载描述符IMAGE_DELAY_IMPORT_DESCRIPTOR

         IMAGE_DELAY_IMPORT_DESCRIPTOR的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct IMAGE_DELAY_IMPORT_DESCRIPTOR STRUCT
{
DWORD Attributes; // 保留
RVA RVA_DLLName; // 指向延迟加载dll的名字字符串的RVA
RVA RVA_ModuleHandle; // 指向DLL句柄的RVA
RVA RVA_DelayIAT; // RVA of the IAT
RVA RVA_DelayINT; // RVA of the INT
RVA RVA_BoundIAT; // RVA of the optional bound IAT
RVA RVA_UnloadIAT; // RVA of optional copy of original IAT
DWORD dwTimeStamp; // 0 if not bound,
// 绑定到DLL的时间戳
} ImgDelayDescr, * PImgDelayDescr;

  • Attributes:双字,暂时未用到,
  • RVA_DLLName:双字,延迟加载dll名称的字符串的地址。
  • RVA_ModuleHandle:双字,延迟加载dll的句柄的地址。
  • RVA_DelayIAT: 延迟加载导入地址表的RVA
  • RVA_DelayINT:延迟加载导入名称表的RVA
  • RVA_BoundIAT:延迟绑定导入地址表的RVA,延迟绑定导入地址表是由IMAGE_THUNK_DATA组成的数组。他和最后一项dwTimeStamp用于最后的绑定阶段。
  • RVA_UnloadIAT:延迟卸载导入地址表由IMAGE_THUNK_DATA组成的数组,程序使用它来卸载dll(包括函数),所用到的参数是原来IAT的精确副本。做释放处理需要做一下几个工作
    • 1)释放函数/dll
    • 2)ModuleHandle清零
    • 3)使用UIAT覆盖IAT。

3.详解延迟加载机制

         使用延迟加载技术的程序,链接器会做一下几个事情:

  • 1)将函数_delayLoadHelper嵌入到可执行模块
  • 2)删除可执行文件导入表的相关内容,避免在初始化时候显式加载dll
  • 3)构造PE相关信息,以便_delayLoadHelper正确的延迟加载函数。
  • 4)调用_delayLoadHelper函数加载dll或获取调用函数地址。
             在_delayLoadHelper函数中,还调用了LoadLibrary加载dll,调用了GetprocessAddress获取函数地址,调用了FreeLibrary去释放dll。

4.利用windbg查看延迟加载导入表

         我们首先用windows xp下的explorer程序举例子。

  • 查看程序加载的模块:lm m explorer
  • 显示PE文件头:!dh start_address -f
  • 查看延迟加载导入的内存空间:dd start_address+offset
  • 比如查看延迟加载导入表第二项的内容dll的ASCII:da address

4.延迟导入的两个问题

1)异常处理

         通过延迟加载机制,未能成功加载的dll或者由于版本问题,未能成功调用的函数,函数_delayLoadHelper会抛出软件异常(其实是API函数剖出的异常)

2)dll卸载

         不能使用FreeLibrary卸载dll,因为在函数_delayLoadHelper中存在卸载函数,

3.延迟加载机制在dll注入的实例。

https://www.anquanke.com/post/id/86919